using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace GameToolkit.Editor
{
    [CustomEditor(typeof(MaterialBlock), true)]
    public class MaterialBlockEditor : UnityEditor.Editor
    {
        //static readonly List<Material> sNoAllocMaterials = new List<Material>(4);
        SerializedProperty m_CBufferValues;
        SerializedProperty m_MaterialInstance;
        SerializedObject m_MaterialInstanceObject;

        Renderer mRenderer;
        Shader mCurrentShader;
        GUIContent mLabel;
        //string[] mShaderPropertyNames;
        List<MaterialInstanceEditor.ShaderValue> mShaderValues;// EMaterialValueType[] mShaderPropertyTypes;
        readonly float[] mVectorValues = new float[4];
        readonly GUIContent[] mVectorValueComponents = new GUIContent[] { new GUIContent("x"), new GUIContent("y"), new GUIContent("z"), new GUIContent("w") };

        HashSet<Shader> mIncludeShaders = new HashSet<Shader>();

        private void OnEnable()
        {
            mLabel = new GUIContent();
            m_MaterialInstance = serializedObject.FindProperty("m_MaterialInstance");
            mRenderer = ((MonoBehaviour)target).GetComponent<Renderer>();
            if (m_MaterialInstance.objectReferenceValue != null && !m_MaterialInstance.hasMultipleDifferentValues)
            {
                m_MaterialInstanceObject = new SerializedObject(m_MaterialInstance.objectReferenceValue);
                m_CBufferValues = m_MaterialInstanceObject.FindProperty("m_Properties");
            }
            else
            {
                m_CBufferValues = serializedObject.FindProperty("m_CBufferValues");
            }
            mIncludeShaders.Clear();
            var rs = ((MonoBehaviour)target).GetComponentsInChildren<Renderer>();
            var mats = CacheUtil.GetListCahce<Material>();
            Shader defaultShader = null;
            foreach (var r in rs)
            {
                r.GetSharedMaterials(mats);
                foreach (var m in mats)
                {
                    if (m != null && m.shader != null)
                    {
                        if (defaultShader == null)
                            defaultShader = m.shader;
                        mIncludeShaders.Add(m.shader);
                    }
                }
                mats.Clear();
            }
            GetMaterialProperties(defaultShader);
        }

        private void OnDisable()
        {
            mCurrentShader = null;
        }

        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();

            //GetMaterialProperties();

            var dirty = false;
            var changevalue = false;
            //if (!serializedObject.isEditingMultipleObjects)
            //{
            //    var hasOverride = ((MonoBehaviour)target).GetComponent<ICBufferValue>() != null;
            //    dirty |= hasOverride ^ m_GetOverrideCBufferValue.boolValue;
            //    m_GetOverrideCBufferValue.boolValue = hasOverride;
            //}
            EditorGUI.BeginDisabledGroup(mIncludeShaders.Count == 0);
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.PrefixLabel("Select Shader");
            var newp = GUILayout.Button(mCurrentShader == null ? "missing" : mCurrentShader.name, "DropDownButton");
            EditorGUILayout.EndHorizontal();
            EditorGUI.EndDisabledGroup();
            if (newp)
            {
                var menu = new GenericMenu();
                GenericMenu.MenuFunction2 sel = (obj) =>
                {
                    GetMaterialProperties(obj as Shader);
                };
                foreach (var s in mIncludeShaders)
                {
                    menu.AddItem(new GUIContent(s.name), s == mCurrentShader, sel, s);
                }
                menu.ShowAsContext();
            }

            EditorGUI.BeginChangeCheck();
            EditorGUILayout.PropertyField(m_MaterialInstance);
            var changeMatInst = EditorGUI.EndChangeCheck();
            var editTarget = m_MaterialInstanceObject == null ? null : m_MaterialInstanceObject.targetObject;
            dirty |= changeMatInst;
            changeMatInst |= editTarget != m_MaterialInstance.objectReferenceValue;
            if (changeMatInst)
            {
                var finalValues = m_MaterialInstanceObject == null ? null : m_CBufferValues;
                m_MaterialInstanceObject = m_MaterialInstance.objectReferenceValue == null || m_MaterialInstance.hasMultipleDifferentValues ?
                    null : new SerializedObject(m_MaterialInstance.objectReferenceValue);
                m_CBufferValues = m_MaterialInstanceObject == null ? serializedObject.FindProperty("m_CBufferValues") : m_MaterialInstanceObject.FindProperty("m_Properties");
            }

            if (!m_MaterialInstance.hasMultipleDifferentValues)
            {
                EditorGUI.BeginChangeCheck();
                //mCBufferValues.DoLayoutList();
                MaterialInstanceEditor.ShaderPropertiesGUILayout(mShaderValues, m_CBufferValues,
                    m_MaterialInstanceObject != null && m_MaterialInstanceObject.targetObject != null ? m_MaterialInstanceObject.targetObject.name : m_CBufferValues.displayName,
                    UpdateMaterialValues);
                changevalue |= EditorGUI.EndChangeCheck();
            }
            else
            {
                EditorGUILayout.HelpBox("Different MaterialInstances.", MessageType.Warning);
            }
            dirty |= changevalue;
            if (dirty)
                serializedObject.ApplyModifiedProperties();

            //if (changeInstancing && m_InstancingOn.boolValue)
            //{
            //    CalculateInstancingID();
            //}
            if (changevalue || changeMatInst)
            {
                if (m_MaterialInstanceObject != null)
                    m_MaterialInstanceObject.ApplyModifiedProperties();
                UpdateMaterialValues();
            }
        }

        void UpdateMaterialValues()
        {
            if (this != null)
            {
                var method = typeof(MaterialBlock).GetMethod("OnMaterialInstanceDirty",
                    System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.InvokeMethod);
                var args = new object[0];
                if (serializedObject.isEditingMultipleObjects)
                {
                    foreach (var t in targets)
                    {
                        var buf = t as MaterialBlock;
                        if (buf != null)
                            method.Invoke(buf, args);
                        //buf.SendMessage("OnMaterialInstanceDirty");
                    }
                }
                else
                {
                    var buf = target as MaterialBlock;
                    if (buf != null)
                        method.Invoke(buf, args);
                    //buf.SendMessage("OnMaterialInstanceDirty");
                }
            }
        }

        void GetMaterialProperties(Shader shader)
        {
            //var shader = mat == null ? null : mat.shader;
            if (shader == null || shader == mCurrentShader)
                return;
            mCurrentShader = shader;
            if (mCurrentShader != null)
            {
                if (mShaderValues == null)
                    mShaderValues = new List<MaterialInstanceEditor.ShaderValue>();
                else
                    mShaderValues.Clear();
                MaterialInstanceEditor.GetShaderProperties(shader, mShaderValues);
            }
        }
    }
}